package com.zhigh.project.security.jwt.authentication;

import com.zhigh.project.security.jwt.authentication.exception.JWTAuthenticationException;
import com.zhigh.project.security.jwt.authentication.principal.AbstractJWTPrincipal;
import com.zhigh.project.security.jwt.authentication.principal.DefaultJWTPrincipal;
import com.zhigh.project.tools.Assertions;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.AuthenticatedPrincipal;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.Transient;
import org.springframework.security.core.authority.AuthorityUtils;

import java.util.Collection;
import java.util.Collections;
import java.util.Optional;

@Transient
public class JWTAuthentication extends AbstractAuthenticationToken implements AuthenticatedPrincipal {

    /**
     * JWT principal
     */
    private final AbstractJWTPrincipal principal;

    public JWTAuthentication(AbstractJWTPrincipal principal) {
        super(
                Optional.ofNullable(principal)
                        .map(AbstractJWTPrincipal::getJwt)
                        .map(JWT::getPayload)
                        .map(JWTPayload::getAuthorities)
                        .map(e -> e.toArray(new String[0]))
                        .map(AuthorityUtils::createAuthorityList)
                        .orElseGet(Collections::emptyList)
        );
        this.principal = principal;
        setAuthenticated(true);
    }

    public JWTAuthentication(AbstractJWTPrincipal principal, Collection<? extends GrantedAuthority> authorities) {
        super(authorities);
        this.principal = principal;
        setAuthenticated(true);
    }

    /**
     * 凭证
     *
     * @return token
     */
    @Override
    public String getCredentials() {
        return Optional.ofNullable(principal).map(AbstractJWTPrincipal::getJwt).map(JWT::getTokenStr).orElse(null);
    }

    /**
     * 凭据
     *
     * @return {@link DefaultJWTPrincipal}
     */
    @Override
    public AbstractJWTPrincipal getPrincipal() {
        return principal;
    }

    @Override
    public boolean isAuthenticated() {
        return super.isAuthenticated() && principal != null;
    }

    @Override
    public JWT getDetails() {
        if (super.getDetails() == null) return null;
        Assertions.assertIsInstanceOf(JWT.class, super.getDetails(), () -> new JWTAuthenticationException("Details only supports " + JWT.class));
        return (JWT) super.getDetails();
    }

    @Override
    public void setDetails(Object details) {
        if (details == null) {
            super.setDetails(null);
        } else {
            Assertions.assertIsInstanceOf(JWT.class, details, () -> new JWTAuthenticationException("Details only supports " + JWT.class));
            super.setDetails(details);
        }
    }
}
